package binding

//常用绑定验证函数
import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// 为空验证(对字符、切片、对象类型有效）
func IsNotNull(val interface{}) bool {
	switch data := val.(type) {
	case string:
		return strings.TrimSpace(data) != ""
	case []interface{}:
		return len(data) > 0
	case nil:
		return false
	}
	return true
}

// 是否为整型
func IsInt(val interface{}) bool {
	switch val.(type) {
	case int, int8, int32, int64, uint, uint32, uint64:
		return true
	}
	return false
}

// 是否为浮点型
func IsFloat(val interface{}) bool {
	switch val.(type) {
	case float32, float64:
		return true
	}
	return false
}

// 正则式处理
// arg=正则表达式
// tag使用方法 match=^\d+$
// 注意：不要在正则式中间加:
func IsMatch(val interface{}, arg string) bool {
	r := regexp.MustCompile(arg)
	return r.MatchString(fmt.Sprint(val))
}

// 长度验证(对数字、字符、切片类型有效)
// tag使用方法 len=10
func IsLen(val interface{}, l int) bool {
	switch data := val.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint32, uint64, float32, float64, string:
		return len([]rune(fmt.Sprint(data))) == l
	case []interface{}:
		return len(data) == l
	}
	return true
}

// 等于(对数字、字符、布尔类型有效）
// tag使用方法 eq=10
func IsEq(val interface{}, arg string) bool {
	return fmt.Sprint(val) == arg
}

// 在范围内(对数字、字符、布尔类型有效）
// tag使用方法 in=a|b|c，多个规则使用|分隔
func IsIn(val interface{}, arg string) bool {
	s := fmt.Sprint(val)
	for _, v := range strings.Split(arg, "|") {
		if s == v {
			return true
		}
	}
	return false
}

// 不在范围内(对数字、字符、布尔类型有效）
// tag使用方法 not-in=a|b|c，多个规则使用|分隔
func IsNotIn(val interface{}, arg string) bool {
	s := fmt.Sprint(val)
	for _, v := range strings.Split(arg, "|") {
		if s == v {
			return false
		}
	}
	return true
}

// 大于(对数字类型有效）
// tag使用方法 gt=10
func IsGreatThan(val interface{}, a float64) bool {
	switch data := val.(type) {
	case int:
		return int64(data) > int64(a)
	case int8:
		return int64(data) > int64(a)
	case int16:
		return int64(data) > int64(a)
	case int32:
		return int64(data) > int64(a)
	case int64:
		return int64(data) > int64(a)
	case uint:
		return int64(data) > int64(a)
	case uint8:
		return int64(data) > int64(a)
	case uint32:
		return int64(data) > int64(a)
	case uint64:
		return int64(data) > int64(a)
	case float32:
		return data-float32(a) > 0
	case float64:
		return data-a > 0
	}
	return true
}

// 大于等于(对数字类型有效）
// tag使用方法 gte=10
func IsGreatThanAndEqual(val interface{}, a float64) bool {
	switch data := val.(type) {
	case int:
		return int64(data) >= int64(a)
	case int8:
		return int64(data) >= int64(a)
	case int16:
		return int64(data) >= int64(a)
	case int32:
		return int64(data) >= int64(a)
	case int64:
		return int64(data) >= int64(a)
	case uint:
		return int64(data) >= int64(a)
	case uint8:
		return int64(data) >= int64(a)
	case uint32:
		return int64(data) >= int64(a)
	case uint64:
		return int64(data) >= int64(a)
	case float32:
		return data-float32(a) >= 0
	case float64:
		return data-a >= 0
	}
	return true
}

// 小于(对数字类型有效）
// tag使用方法 lt=10
func IsLessThan(val interface{}, a float64) bool {
	switch data := val.(type) {
	case int:
		return int64(data) < int64(a)
	case int8:
		return int64(data) < int64(a)
	case int16:
		return int64(data) < int64(a)
	case int32:
		return int64(data) < int64(a)
	case int64:
		return int64(data) < int64(a)
	case uint:
		return int64(data) < int64(a)
	case uint8:
		return int64(data) < int64(a)
	case uint32:
		return int64(data) < int64(a)
	case uint64:
		return int64(data) < int64(a)
	case float32:
		return data-float32(a) < 0
	case float64:
		return data-a < 0
	}
	return true
}

// 小于等于(对数字类型有效）
// tag使用方法 lte=10
func IsLessThanAndEuqal(val interface{}, a float64) bool {
	switch data := val.(type) {
	case int:
		return int64(data) <= int64(a)
	case int8:
		return int64(data) <= int64(a)
	case int16:
		return int64(data) <= int64(a)
	case int32:
		return int64(data) <= int64(a)
	case int64:
		return int64(data) <= int64(a)
	case uint:
		return int64(data) <= int64(a)
	case uint8:
		return int64(data) <= int64(a)
	case uint32:
		return int64(data) <= int64(a)
	case uint64:
		return int64(data) <= int64(a)
	case float32:
		return data-float32(a) <= 0
	case float64:
		return data-a <= 0
	}
	return true
}

// 最小长度(对字符、数字、切片类型有效）
// tag使用方法 min=10
func IsMin(val interface{}, l int) bool {
	switch data := val.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint32, uint64, float32, float64, string:
		return len([]rune(fmt.Sprint(data))) >= l
	case []interface{}:
		return len(data) >= l
	}
	return true
}

// 最大长度度(对字符、数字、切片类型有效）
// tag使用方法 max=10
func IsMax(val interface{}, l int) bool {
	switch data := val.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint32, uint64, float32, float64, string:
		return len([]rune(fmt.Sprint(data))) <= l
	case []interface{}:
		return len(data) <= l
	}
	return true
}

// 日期 格式 2006-01-02
// tag使用方法 date
func IsDate(val interface{}) bool {
	switch data := val.(type) {
	case string:
		_, err := time.Parse("2006-01-02", data)
		return err == nil
	}
	return false
}

// 日期时间 格式 2006-01-02 15:04:05
// tag使用方法 datetime
func IsDatetime(val interface{}) bool {
	switch data := val.(type) {
	case string:
		_, err := time.Parse("2006-01-02 15:04:05", data)
		return err == nil
	}
	return false
}

// 时间
func IsHourMinute(val interface{}) bool {
	switch data := val.(type) {
	case string:
		_, err := time.Parse("15:04", data)
		return err == nil
	}
	return false
}

/*
 * 验证所给手机号码是否符合手机号的格式.
 * 移动: 134、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、178(4G)、147(上网卡)；
 * 联通: 130、131、132、155、156、185、186、176(4G)、145(上网卡)、175；
 * 电信: 133、153、180、181、189 、177(4G)；
 * 卫星通信:  1349
 * 虚拟运营商: 170、171,173
 * 2018新增: 16x, 19x
 */
//tag使用方法 mobile
func IsMobile(val interface{}) bool {
	return IsMatch(val, `^1[3,4,5,6,7,8,9][\d]{9}$`)
	//return IsMatch(val, `^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,1,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`)
}

// 国内座机电话号码："XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
// tag使用方法 tel
func IsTel(val interface{}) bool {
	return IsMatch(val, `^((\d{3,4})|\d{3,4}-)?\d{7,8}$`)
}

// 腾讯QQ号，从10000开始
// tag使用方法 tel
func IsQQ(val interface{}) bool {
	return IsMatch(val, `^[1-9][0-9]{4,}$`)
}

// 邮政编码
// tag使用方法 postcode
func IsPostcode(val interface{}) bool {
	return IsMatch(val, `^\d{6}$`)
}

// 帐号规则(字母开头，只能包含字母、数字和下划线，长度在6~18之间)
// tag使用方法 account
func IsAccount(val interface{}) bool {
	return IsMatch(val, `^\w{5,17}$`)
}

// 通用密码(任意可见字符，长度在6~18之间)
// tag使用方法 password
func IsPassword(val interface{}) bool {
	return IsMatch(val, `^[\w\S]{6,18}$`)
}

// 中等强度密码(在弱密码的基础上，必须包含大小写字母和数字)
// tag使用方法 password2
func IsPassword2(val interface{}) bool {
	return IsMatch(val, `^[\w\S]{6,18}$`) && IsMatch(val, `[a-z]+`) && IsMatch(val, `[A-Z]+`) && IsMatch(val, `\d+`)
}

// 强等强度密码(在弱密码的基础上，必须包含大小写字母、数字和特殊字符)
// tag使用方法 password3
func IsPassword3(val interface{}) bool {
	return IsPassword2(val) && IsMatch(val, `[^a-zA-Z0-9]+`)
}

// 邮件
// tag使用方法 email
func IsEmail(val interface{}) bool {
	return IsMatch(val, `^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$`)
}

// URL
// tag使用方法 url
func IsUrl(val interface{}) bool {
	return IsMatch(val, `^(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`)
}

// domain
// tag使用方法 domain
func IsDomain(val interface{}) bool {
	return IsMatch(val, `^([0-9a-zA-Z][0-9a-zA-Z-]{0,62}\.)+([0-9a-zA-Z][0-9a-zA-Z-]{0,62})\.?$`)
}

// MAC地址
// tag使用方法 mac
func IsMac(val interface{}) bool {
	return IsMatch(val, `^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`)
}

// IP4地址
// tag使用方法 ip4
func IsIP4(val interface{}) bool {
	return IsMatch(val, `^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$`)
}

// IP6地址
// tag使用方法 ip6
func IsIP6(val interface{}) bool {
	return IsMatch(val, `^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$|^:((:[\da-fA-F]{1,4}){1,6}|:)$|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)$|^([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,4}|:)$|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)$|^([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,2}|:)$|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?$|^([\da-fA-F]{1,4}:){6}:$`)
}

// IP地址(ip4和ip6均可）
// tag使用方法 ip
func IsIP(val interface{}) bool {
	return IsIP4(val) || IsIP6(val)
}

/*
   公民身份证号
   xxxxxx yyyy MM dd 375 0     十八位
   xxxxxx   yy MM dd  75 0     十五位

   地区：[1-9]\d{5}
   年的前两位：(18|19|([23]\d))      1800-2399
   年的后两位：\d{2}
   月份：((0[1-9])|(10|11|12))
   天数：(([0-2][1-9])|10|20|30|31) 闰年不能禁止29+

   三位顺序码：\d{3}
   两位顺序码：\d{2}
   校验码：   [0-9Xx]

   十八位：^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
   十五位：^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$

   总：
   (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
*/
//tag使用方法 id-number
func IsIDNumber(val interface{}) bool {
	value, ok := val.(string)
	if !ok {
		return false
	}
	value = strings.ToUpper(strings.TrimSpace(value))
	// 18位长检测
	if len(value) != 18 {
		return false
	}
	// 加权因子
	weightFactor := [...]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
	// 校验码
	checkCode := [...]byte{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}
	last := value[17]
	num := 0
	for i := 0; i < 17; i++ {
		tmp, err := strconv.Atoi(string(value[i]))
		if err != nil {
			return false
		}
		num = num + tmp*weightFactor[i]
	}
	resisue := num % 11
	if checkCode[resisue] != last {
		return false
	}
	return IsMatch(value, `(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)`)
}

// LUHN规则(银行卡号验证规则)
// tag使用方法 luhn
func IsLuHn(val interface{}) bool {
	value, ok := val.(string)
	if !ok {
		return false
	}
	var sum = 0
	var nDigits = len(value)
	var parity = nDigits % 2

	for i := 0; i < nDigits; i++ {
		var digit = int(value[i] - 48)
		if i%2 == parity {
			digit *= 2
			if digit > 9 {
				digit -= 9
			}
		}
		sum += digit
	}
	return sum%10 == 0
}

// 数组是否有重复
func IsArrayRepeat(val interface{}) bool {
	switch data := val.(type) {
	case []string:
		var m = make(map[string]bool)
		for _, item := range data {
			if m[item] {
				return true
			}
			m[item] = true
		}
	}
	return false
}
